package types

import (
	"encoding/json"
	"fmt"

	"gopkg.in/src-d/go-git.v4/plumbing"
)

// Rawer get raw
type Rawer interface {
	Hiter
	Put([]byte) error
	Open(bool) error
	Close() error
	Split() error
	GetRaw(Selector) (raw []byte, err error)
	Sync() error
	Remove() error
	Name() string
	Reset() error
}

// SomeRawer some rawer
type SomeRawer struct {
	some []Rawer
}

func nilParse([]byte) ([]byte, plumbing.ObjectType, *Header32, error) {
	return []byte{}, plumbing.InvalidObject, nil, fmt.Errorf("miss parser")
}

var (
	//RawParse globe raw parse to object
	RawParse = nilParse
)

// Open interface
func (rs *SomeRawer) Open(w bool) (err error) {
	for _, r := range rs.some {
		err = r.Open(w)
		if err != nil {
			return
		}
	}
	return
}

// Reset interface
func (rs *SomeRawer) Reset() (err error) {
	for _, r := range rs.some {
		cerr := r.Reset()
		if cerr != nil {
			err = cerr
		}
	}
	return
}

// Sync interface
func (rs *SomeRawer) Sync() (err error) {
	for _, r := range rs.some {
		err = r.Sync()
		if err != nil {
			return
		}
	}
	return
}

// Close interface
func (rs *SomeRawer) Close() (err error) {
	for _, r := range rs.some {
		err = r.Close()
		if err != nil {
			return
		}
	}
	return
}

// Hit interface
func (rs *SomeRawer) Hit(h Hash) bool {
	for _, r := range rs.some {
		if r.Hit(h) {
			return true
		}
	}
	return false
}

// Name interface
func (rs *SomeRawer) Name() string {
	var ns []string
	for _, r := range rs.some {
		ns = append(ns, r.Name())
	}
	bs, _ := json.Marshal(ns)
	return string(bs)
}

// GetRaw inferace implement
func (rs *SomeRawer) GetRaw(s Selector) (raw []byte, err error) {
	err = ErrorNoFind
	for _, r := range rs.some {
		var hash Hash
		hash, err = Selector2Hash(s)
		if err != nil {
			return
		}
		if r.Hit(hash) {
			raw, err = r.GetRaw(s)
			if err == ErrorNoFind {
				continue
			}
			return
		}
	}
	return
}

// Split split
func (rs *SomeRawer) Split() error {
	return nil
}

// Put put raw to some pack file
func (rs *SomeRawer) Put(raw []byte) (err error) {
	for _, v := range rs.some {
		err = v.Put(raw)
		if err != nil {
			return
		}
	}
	return
}

// Remove remove some rawer
func (rs *SomeRawer) Remove() (err error) {
	for _, v := range rs.some {
		err = v.Remove()
		if err != nil {
			return
		}
	}
	return
}

// NewRawer new from geters
func NewRawer(rs []Rawer) (ps SomeRawer) {
	ps = SomeRawer{}
	for _, r := range rs {
		ps.some = append(ps.some, r)
	}
	return
}

// Some  export some
func (rs *SomeRawer) Some() (rrs []Rawer) {
	for _, v := range rs.some {
		rrs = append(rrs, v)
	}
	return
}
